/***************************************************************************
                          octopus.c  -  description
                             -------------------
    begin                : Aug  2007
    author               : Benedikt Sauter, sauter@embedded-projects.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software, you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License           *
 *   version 2.1 as published by the Free Software Foundation.             *
 *                                                                         *
 ***************************************************************************/

/**
    \mainpage liboctopus API documentation

    Library to talk to octopus devices. You find the latest versions of liboctopus at
    http://www.embedded-projects.net/octopus

    The library is easy to use. Have a look at this short example:
    \include simple.c

    More examples can be found in the "examples" directory.
*/
/** \addtogroup liboctopus */
/* @{ */

#include <usb.h>
#include <string.h>
#include <errno.h>

#include "octopus.h"
#include "protocol.h"

#define octopus_error_return(code, str) do {  \
        octopus->error_str = str;             \
	return code;                       \
	} while(0);


/*
 * initial octopus handle before use octopus_open
 */
int octopus_init(struct octopus_context *octopus)
{
   if (octopus == NULL)
      octopus_error_return(-1,"octopus not valid");

   octopus->error_str = NULL;
   octopus->usb_handle = NULL;
  
   return 1;
}

/*
 * open first octopus on usb
 */
int octopus_open(struct octopus_context *octopus)
{
   if (octopus_open_id(octopus,VID,PID) < 0)
      octopus_error_return(-1,"could not found octopus device with pid and vid");
   
   return 1;
}

/*
 * open octopus with a given usb vendor and product id
 */
int octopus_open_id(struct octopus_context *octopus, int vendor, int product)
{
   struct usb_bus *busses;
   struct usb_bus *bus;
   struct usb_device *dev;

   usb_init();

   if (usb_find_busses() < 0)
      octopus_error_return(-1, "usb_find_busses() failed");
    
   if (usb_find_devices() < 0)
      octopus_error_return(-2, "usb_find_devices() failed");
  
   busses = usb_get_busses();
   
   if (busses == NULL)
      octopus_error_return(-3, "usb_get_busses() failed");
  
   for (bus = busses; bus; bus = bus->next)
   {
      for (dev = bus->devices; dev; dev = dev->next)
      {
         if (dev->descriptor.idVendor == vendor && dev->descriptor.idProduct == product) 
         {
	         octopus_open_dev(octopus,dev);
	         return 1;
         }
      }
   }
   octopus_error_return(-4,"could not found octopus device with pid and vid");
   return -1;
}

/*
 * open octopus with a given usb name description (not tested!)
 */
int octopus_open_serial(struct octopus_context *octopus, char * serial)
{
   struct usb_bus *busses;
   struct usb_dev_handle* usb_handle;
   struct usb_bus *bus;
   struct usb_device *dev;
   char usbserial[64];
   int length=0;

   usb_init();

   if (usb_find_busses() < 0)
      octopus_error_return(-1, "usb_find_busses() failed");
   if (usb_find_devices() < 0)
      octopus_error_return(-2, "usb_find_devices() failed");

   busses = usb_get_busses();
   if(busses==NULL)
      octopus_error_return(-3, "usb_get_busses() failed");

   for (bus = busses; bus; bus = bus->next)
   {
      for (dev = bus->devices; dev; dev = dev->next)
      {
         usb_handle = usb_open(dev);
         usb_set_configuration(usb_handle,0);
         length = usb_get_string_simple(usb_handle,3,usbserial,64);
         usb_close(usb_handle);

         if (strncmp(serial,usbserial,length) == 0)
         {
            octopus_open_dev(octopus,dev);
            return 1;
         }
      }
   }
   
   octopus_error_return(-4,"could not found octopus device with serial number");
   return -1;
}

/*
 * open octopus with a libusb dev handle
 */
int octopus_open_dev(struct octopus_context *octopus, struct usb_device *dev)
{
   if (octopus == NULL)
      octopus_error_return(-1,"octopus handle is wrong or missing");

   if (dev == NULL)
      octopus_error_return(-2,"device handle is wrong or missing");

   if (!(octopus->usb_handle = usb_open(dev)))
      octopus_error_return(-3,"can't open usb device");

   if (usb_set_configuration (octopus->usb_handle,dev->config[0].bConfigurationValue) < 0)
      octopus_error_return(-4,"can't set configuration for given usb device");

   if (usb_claim_interface(octopus->usb_handle, 0)  < 0)
      octopus_error_return(-5,"can't claim interface for given usb device");

   if (usb_set_altinterface(octopus->usb_handle,0)  < 0)
      octopus_error_return(-6,"can't set altinterface for given usb device");

   return 1;
}

/*
 * close a open octopus handle
 */
int octopus_close(struct octopus_context *octopus)
{
   if (octopus == NULL)
      octopus_error_return(-1,"octopus is not a valid handle");

   if (usb_close(octopus->usb_handle) < 0)
      octopus_error_return(-2,usb_strerror());

   return 1;
}

/*
 * send message to octopus
 */
int octopus_message(struct  octopus_context *octopus, char *msg,
		    int msglen, char *answer, int answerlen)
{
   int timeout=0; 
   
   if (usb_bulk_write(octopus->usb_handle,1,msg,msglen,100) < msglen)
      octopus_error_return(-1,"transfer error occurred"); 

   if (answerlen>0){    
      while(1)
      {
         if(usb_bulk_read(octopus->usb_handle,0x81,(char*)answer,answerlen,100)>0)
            break;
         if(timeout>=1000)
            octopus_error_return(-2,"receive error occurred");
         timeout++;
      }

      if(answer[1]==RSP_OK)
      {
         return 1;
      } 
      else if (answer[1]==RSP_UNKOWN_CMD) 
      {
         octopus_error_return(-1,"unkown command"); 
      } 
      else if (answer[1]==RSP_UNKOWN_PIN) 
      {
         octopus_error_return(-2,"unkown pin"); 
      } 
      else if (answer[1]==RSP_WRONG_PIN_CONFIG) 
      {
         octopus_error_return(-3,"pin is in wrong configuration"); 
      } 
      else if (answer[1]==RSP_ERROR) 
      {
         octopus_error_return(-4,"error in usb device"); 
      } 
      else if (answer[1]==RSP_TIMEOUT) 
      {
         octopus_error_return(-4,"timeout error in usb device"); 
      } 
      else 
      {
         octopus_error_return(-5,"unknown error"); 
      }      
   }
   return 1;
}

/*
 * get internal device description of octopus (octopus_1, octocan_01)
 */
char * octopus_get_hwdesc(struct octopus_context *octopus, char *desc)
{

   char msg[] = {CMD_GET_HW_ID,0x00,0x00};
   int i;  
   char answer[64];
  
   for (i=0;i<64;i++)
      answer[i]=0;

   if (octopus_message(octopus,msg,3,answer,14) == RSP_OK)
   {
      if ((int)answer[2] > 0)
         memcpy(desc,answer+3,(int)answer[2]);
      return desc;
   } 
   else 
   {
      octopus_error_return(NULL,octopus->error_str);
   }
   return desc;
}

/*
 * use pin as gpio
 */
int octopus_io_init(struct octopus_context *octopus, int pin)
{
   char answer[3];
   char msg[] = {CMD_IO_INIT_PIN,0x01,0x00};
   msg[2] = (char)pin;
   if (octopus_message(octopus,msg,3,answer,3) > 0)
      return 1;
   else return -1;
}


int octopus_io_init_port(struct octopus_context *octopus, int port)
{
  char msg[] = {CMD_IO_INIT_PORT,0x01,0x00};
  char answer[3];
  msg[2] = (char)port;
  if(octopus_message(octopus,msg,3,answer,3)>0)
    return 1;
  else return -1;
}

int octopus_io_set_port_direction_out(struct octopus_context *octopus, int port,unsigned char mask)
{
  char answer[3];
  char msg[] = {CMD_IO_PORT_DIRECTION_OUT,0x02,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = mask;
  if(octopus_message(octopus,msg,4,answer,3))
    return 1;
  else return -1;
}

int octopus_io_set_port_direction_in(struct octopus_context *octopus, int port, unsigned char mask)
{
  char answer[3];
  char msg[] = {CMD_IO_PORT_DIRECTION_IN,0x02,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = mask;
  if(octopus_message(octopus,msg,4,answer,3))
    return 1;
  else return -1;
}


int octopus_io_set_port_direction_tri(struct octopus_context *octopus, int port, unsigned char mask)
{
  char answer[3];
  char msg[] = {CMD_IO_PORT_DIRECTION_TRI,0x02,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = mask;
  if(octopus_message(octopus,msg,4,answer,3))
    return 1;
  else return -1;
}

/*
 * use pin as output
 */
int octopus_io_set_pin_direction_out(struct octopus_context *octopus, int pin)
{
   char answer[3];
   char msg[] = {CMD_IO_PIN_DIRECTION_OUT,0x01,0x00};
   msg[2] = (char)pin;
   if (octopus_message(octopus,msg,3,answer,3) == RSP_OK)
      return 1;
   else 
      octopus_error_return(-1,octopus->error_str);
}


int octopus_io_set_pin_direction_in(struct octopus_context *octopus, int pin)
{
  char answer[3];
  char msg[] = {CMD_IO_PIN_DIRECTION_IN,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,3)==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}



int octopus_io_set_pin_direction_tri(struct octopus_context *octopus, int pin)
{
  char answer[3];
  char msg[] = {CMD_IO_PIN_DIRECTION_TRI,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,3)==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}

unsigned char octopus_io_get_port(struct octopus_context *octopus, int port)
{
  char answer[3];
  char msg[] = {CMD_IO_PORT_GET,0x01,0x00};
  msg[2] = (char)port;
  if(octopus_message(octopus,msg,3,answer,3)==RSP_OK)
    return (unsigned char) answer[2];
  else 
    octopus_error_return((unsigned char)NULL,octopus->error_str);
}


int octopus_io_set_port(struct octopus_context *octopus, int port, unsigned char value)
{
  char answer[3];
  char msg[] = {CMD_IO_PORT_SET,0x01,0x00,0x00};
  msg[2] = (char)port;
  msg[3] = (char)value;
  if(octopus_message(octopus,msg,4,answer,3))
    return 1;
  else return -1;
}


int octopus_io_set_pin(struct octopus_context *octopus, int pin, int value)
{
  char answer[3];
  char msg[] = {CMD_IO_PIN_SET,0x01,0x00,0x00};
  msg[2] = (char)pin;
  msg[3] = (char)value;
  if(octopus_message(octopus,msg,4,answer,3)==RSP_OK)
    return 1;
  else 
    octopus_error_return(-1,octopus->error_str);
}  


int octopus_io_get_pin(struct octopus_context *octopus, int pin)
{
  char answer[3];
  char msg[] = {CMD_IO_PIN_GET,0x01,0x00};
  msg[2] = (char)pin;
  msg[3] = 0x00;
  if(octopus_message(octopus,msg,3,answer,3)==RSP_OK)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);
}



/// part: adc
int octopus_adc_init(struct octopus_context *octopus, int pin)
{
  char answer[3];
  char msg[] = {CMD_ADC_INIT_PIN,0x01,0x00};
  msg[2] = (char)pin;
  if(octopus_message(octopus,msg,3,answer,3)>0)
    return 1;
  else return -1;
}



int octopus_adc_get(struct octopus_context *octopus, int pin)
{
  unsigned char answer[4];
  char msg[] = { CMD_ADC_GET, 0x01, 0x00 };
  msg[2] = (char) pin;

  if (octopus_message (octopus, msg, 3, (char *)answer, sizeof (answer)) >0)
  {
     unsigned int low = (unsigned int)answer[3];
     unsigned int high = (unsigned int)answer[2];

     return((int)((unsigned int)(low | (high << 8))));
  }
  else
     return -1;
}

int octopus_adc_ref(struct octopus_context *octopus, int ref)
{
  char answer[3];
  char msg[] = {CMD_ADC_REF,0x01,0x00};
  msg[2] = (char)ref;
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return (int)answer[1];
  else 
    octopus_error_return(-1,octopus->error_str);
}



int octopus_adc_get_bulk(struct octopus_context *octopus, 
  int pin, int number, int * buf)
{
  return 1;
}

int octopus_i2c_init(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_I2C_INIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;
}

int octopus_i2c_deinit(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_I2C_DEINIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;
}


int octopus_i2c_set_bitrate(struct octopus_context *octopus, int speed)
{
  char answer[2];
  char msg[] = {CMD_I2C_BITRATE,0x01,0x00};
  msg[2] = (char)speed;
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}


int octopus_i2c_send_bytes(struct octopus_context *octopus, 
  unsigned char *buf, int len, int timeout)
{
  if(len>60){
    return -1; // max 60 bytes
  }
  
  char answer[2];

  // cmd = cmd,len,timeout,address,data1,data2,..)
  char msg[64] = {CMD_I2C_SEND_BYTES,0x01,0x00};
  msg[1] = (char)(len+4);
  msg[2] = (char)timeout;
  //msg[3] = (char)address;

  // build send array

  int i;
  for(i=0;i<len;i++)
    msg[i+4]=(char)buf[i];

  if(octopus_message(octopus,msg,len+4,answer,2)>0)
    return (int)answer[2];
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}


int octopus_i2c_send_byte(struct octopus_context *octopus, 
  unsigned char buf,int timeout)
{
  return octopus_i2c_send_bytes(octopus,&buf,1,1); // 1ms
}


unsigned char octopus_i2c_receive_byte(struct octopus_context *octopus, 
  int address,int timeout)
{
  char tmp[1];

  if(octopus_i2c_receive_bytes(octopus,address,tmp,1,1)>0)
    return tmp[1];
  else
    return -1;
}


int octopus_i2c_receive_bytes(struct octopus_context *octopus, 
  int address,char *buf, int len, int timeout)
{
  if(len>60){
    return -1; // max 60 bytes
  }
 
  char msg[64] = {CMD_I2C_RECV_BYTES,0x01,0x00};
  msg[1] = (char)(len+4);
  msg[2] = (char)timeout;
  msg[3] = (char)address;

  char answer[64];
  int i;

  if(octopus_message(octopus,msg,4,answer,len+3)>0){
    for(i=0;i<len;i++)
      buf[i]=answer[i+3];
    return (int)answer[2];
  }
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}

int octopus_i2c_send_start(struct octopus_context *octopus)
{
  char answer[3];
  char msg[] = {CMD_I2C_SEND_START,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

  return 1;
}

int octopus_i2c_send_stop(struct octopus_context *octopus)
{
  char answer[3];
  char msg[] = {CMD_I2C_SEND_STOP,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

  return 1;
}

/// part: SPI
int octopus_spi_init(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_SPI_INIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

}

int octopus_spi_deinit(struct octopus_context *octopus)
{
  char answer[2];
  char msg[] = {CMD_SPI_DEINIT,0x00,0x00};
  if(octopus_message(octopus,msg,3,answer,2)>0)
    return 1;
  else return -1;

}

int octopus_spi_speed(struct octopus_context *octopus, int speed)
{
    return 0;
}


int octopus_spi_send(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60){
    return -1; // max 60 bytes
  }
  
  char answer[2];

  // cmd = cmd,len,timeout,address,data1,data2,..)
  char msg[64] = {CMD_SPI_SEND,0x01,0x00};
  msg[1] = (char)(length);

  // build send array
  int i;
  for(i=0;i<length;i++)
    msg[i+2]=(char)buf[i];

  if(octopus_message(octopus,msg,2+length,answer,2)>0)
    return 1; 
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;
}

int octopus_spi_recv(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60){
    return -1; // max 60 bytes
  }
  
  char answer[64];

  char msg[64] = {CMD_SPI_RECV,0x01,0x00};
  msg[1] = (char)(length);
  
  int i;

  if(octopus_message(octopus,msg,2,answer,2+length)>0) {
    for(i=0;i<length;i++)
      buf[i]=answer[i+2];
   
    return 1; 
  }
  else 
    octopus_error_return(-1,octopus->error_str);
}

int octopus_spi_send_and_recv(struct octopus_context *octopus, unsigned char * buf, int length)
{
  if(length>60){
    return -1; // max 60 bytes
  }
  
  char answer[2];

  // cmd = cmd,len,timeout,address,data1,data2,..)
  char msg[64] = {CMD_SPI_SEND_AND_RECV,0x01,0x00};
  msg[1] = (char)(length);

  // build send array
  int i;
  for(i=0;i<length;i++)
    msg[i+2]=(char)buf[i];

  if(octopus_message(octopus,msg,2+length,answer,2+length)>0) {
    
    for(i=0;i<length;i++)
      buf[i]=answer[i+2];
 
    return 1; 
  }
  else 
    octopus_error_return(-1,octopus->error_str);

  return 1;

}

/// part:  Flash 93c46
int octopus_93c46_init(struct octopus_context *octopus)
{
   char answer[3];
   char msg[] = {CMD_EXTERNAL_DEVICE,0x00,CMD_EXTERNAL_93C46,CMD_93C46_INIT};
   msg[1] = 4;
   if(octopus_message(octopus,msg,4,answer,3)>0)
      return 1;
   else return -1;
}

int octopus_93c46_deinit(struct octopus_context *octopus)
{
   char answer[3];
   char msg[] = {CMD_EXTERNAL_DEVICE,CMD_EXTERNAL_93C46,CMD_93C46_DEINIT};
   if (octopus_message(octopus,msg,3,answer,3) > 0)
      return 1;
   else return -1;
}

int octopus_93c46_write(struct octopus_context *octopus, unsigned char address, int length, unsigned char * buf)
{
   /*
   if(length>59){
      return -1; // max 60 bytes
   }
   */
   
   char answer[3];

   // cmd = cmd,len,timeout,address,data1,data2,..)
   char msg[150] = {CMD_EXTERNAL_DEVICE,0x00,CMD_EXTERNAL_93C46,CMD_93C46_WRITE};
   msg[1] = (unsigned char)(6 + length);
   msg[4] = (unsigned char)(address);
   msg[5] = (unsigned char)(length);

   // build send array
   int i;
   for(i=0;i<length;i++)
      msg[i+6]=(char)buf[i];

   if(octopus_message(octopus,msg,6+length,answer,3)>0)
      return 1; 
   else 
      octopus_error_return(-1,octopus->error_str);

   return 1;
}

int octopus_93c46_read(struct octopus_context *octopus, unsigned char address, int length, unsigned char * buf)
{
   /*
   if(length>59)
   {
      return -1; // max 60 bytes
   }*/

   char answer[150];

   char msg[6] = {CMD_EXTERNAL_DEVICE,0x00,CMD_EXTERNAL_93C46,CMD_93C46_READ};
   
   msg[1] = 6;
   msg[4] = (unsigned char)(address);
   msg[5] = (unsigned char)(length);

   int i;

   if (octopus_message(octopus, msg, 6, answer, 2 + length) > 0)
   {    
      for(i=0;i<length;i++)
         buf[i]=answer[i+2];

      return 1; 
   }
   else 
      octopus_error_return(-1,octopus->error_str);
}


/// part:  PWM
int octopus_pwm_init(struct octopus_context *octopus, int pin)
{
   char answer[3];
   char msg[] = {CMD_PWM_INIT_PIN,0x01,0x00};
   msg[2] = (char)pin;
   if(octopus_message(octopus,msg,3,answer,3)>0)
      return 1;
   else return -1;
}

int octopus_pwm_deinit(struct octopus_context *octopus, int pin)
{
   char answer[3];
   char msg[] = {CMD_PWM_DEINIT_PIN,0x01,0x00};
   msg[2] = (char)pin;
   if(octopus_message(octopus,msg,3,answer,3)>0)
      return 1;
   else return -1;
}

int octopus_pwm_speed(struct octopus_context *octopus, int pin, int speed)
{
   char answer[3];
   char msg[] = {CMD_PWM_SPEED,0x02,0x00,0x00};
   msg[2] = (char)pin;
   msg[3] = (char)speed;
   if(octopus_message(octopus,msg,4,answer,3)>0)
      return 1;
   else return -1;
}

int octopus_pwm_value(struct octopus_context *octopus, int pin, unsigned char value)
{
   char answer[3];
   char msg[] = {CMD_PWM_VALUE,0x02,0x00,0x00};
   msg[2] = (char)pin;
   msg[3] = (char)value;
   if(octopus_message(octopus,msg,4,answer,3)>0)
      return 1;
   else return -1;
}

/// part: UART
int octopus_uart_init(struct octopus_context *octopus, int uartport){ return 0; }
int octopus_uart_deinit(struct octopus_context *octopus, int uartport){ return 0; }

int octopus_uart_default(struct octopus_context *octopus, int uartport){return 0;}
int octopus_uart_speed(struct octopus_context *octopus, int uartport, int speed){return 0;}
int octopus_uart_stopbits(struct octopus_context *octopus, int uartport, int stopbits){return 0;}
int octopus_uart_databits(struct octopus_context *octopus, int uartport, int databits){return 0;}
int octopus_uart_parity(struct octopus_context *octopus, int uartport, int parity){return 0;}

int octopus_uart_send(struct octopus_context *octopus, int uartport, unsigned char * buf, int length){return 0;}
int octopus_uart_recv(struct octopus_context *octopus, int uartport, unsigned char * buf, int length){return 0;}


